/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_mips_shared_Assembler_mips_shared_h
#define jit_mips_shared_Assembler_mips_shared_h

#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/MathAlgorithms.h"

#include "jit/CompactBuffer.h"
#include "jit/IonCode.h"
#include "jit/JitCompartment.h"
#include "jit/JitSpewer.h"
#include "jit/mips-shared/Architecture-mips-shared.h"
#include "jit/shared/Assembler-shared.h"
#include "jit/shared/IonAssemblerBuffer.h"

namespace js {
namespace jit {

static constexpr Register zero = { Registers::zero };
static constexpr Register at = { Registers::at };
static constexpr Register v0 = { Registers::v0 };
static constexpr Register v1 = { Registers::v1 };
static constexpr Register a0 = { Registers::a0 };
static constexpr Register a1 = { Registers::a1 };
static constexpr Register a2 = { Registers::a2 };
static constexpr Register a3 = { Registers::a3 };
static constexpr Register a4 = { Registers::ta0 };
static constexpr Register a5 = { Registers::ta1 };
static constexpr Register a6 = { Registers::ta2 };
static constexpr Register a7 = { Registers::ta3 };
static constexpr Register t0 = { Registers::t0 };
static constexpr Register t1 = { Registers::t1 };
static constexpr Register t2 = { Registers::t2 };
static constexpr Register t3 = { Registers::t3 };
static constexpr Register t4 = { Registers::ta0 };
static constexpr Register t5 = { Registers::ta1 };
static constexpr Register t6 = { Registers::ta2 };
static constexpr Register t7 = { Registers::ta3 };
static constexpr Register s0 = { Registers::s0 };
static constexpr Register s1 = { Registers::s1 };
static constexpr Register s2 = { Registers::s2 };
static constexpr Register s3 = { Registers::s3 };
static constexpr Register s4 = { Registers::s4 };
static constexpr Register s5 = { Registers::s5 };
static constexpr Register s6 = { Registers::s6 };
static constexpr Register s7 = { Registers::s7 };
static constexpr Register t8 = { Registers::t8 };
static constexpr Register t9 = { Registers::t9 };
static constexpr Register k0 = { Registers::k0 };
static constexpr Register k1 = { Registers::k1 };
static constexpr Register gp = { Registers::gp };
static constexpr Register sp = { Registers::sp };
static constexpr Register fp = { Registers::fp };
static constexpr Register ra = { Registers::ra };

static constexpr Register ScratchRegister = at;
static constexpr Register SecondScratchReg = t8;

// Helper classes for ScratchRegister usage. Asserts that only one piece
// of code thinks it has exclusive ownership of each scratch register.
struct ScratchRegisterScope : public AutoRegisterScope
{
    explicit ScratchRegisterScope(MacroAssembler& masm)
      : AutoRegisterScope(masm, ScratchRegister)
    { }
};
struct SecondScratchRegisterScope : public AutoRegisterScope
{
    explicit SecondScratchRegisterScope(MacroAssembler& masm)
      : AutoRegisterScope(masm, SecondScratchReg)
    { }
};

// Use arg reg from EnterJIT function as OsrFrameReg.
static constexpr Register OsrFrameReg = a3;
static constexpr Register ArgumentsRectifierReg = s3;
static constexpr Register CallTempReg0 = t0;
static constexpr Register CallTempReg1 = t1;
static constexpr Register CallTempReg2 = t2;
static constexpr Register CallTempReg3 = t3;

static constexpr Register IntArgReg0 = a0;
static constexpr Register IntArgReg1 = a1;
static constexpr Register IntArgReg2 = a2;
static constexpr Register IntArgReg3 = a3;
static constexpr Register IntArgReg4 = a4;
static constexpr Register IntArgReg5 = a5;
static constexpr Register IntArgReg6 = a6;
static constexpr Register IntArgReg7 = a7;
static constexpr Register GlobalReg = s6; // used by Odin
static constexpr Register HeapReg = s7; // used by Odin

static constexpr Register PreBarrierReg = a1;

static constexpr Register InvalidReg = { Registers::invalid_reg };
static constexpr FloatRegister InvalidFloatReg;

static constexpr Register StackPointer = sp;
static constexpr Register FramePointer = InvalidReg;
static constexpr Register ReturnReg = v0;
static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;

// A bias applied to the GlobalReg to allow the use of instructions with small
// negative immediate offsets which doubles the range of global data that can be
// accessed with a single instruction.
static const int32_t WasmGlobalRegBias = 32768;

// Registers used in the GenerateFFIIonExit Enable Activation block.
static constexpr Register WasmIonExitRegCallee = t0;
static constexpr Register WasmIonExitRegE0 = a0;
static constexpr Register WasmIonExitRegE1 = a1;

// Registers used in the GenerateFFIIonExit Disable Activation block.
// None of these may be the second scratch register (t8).
static constexpr Register WasmIonExitRegD0 = a0;
static constexpr Register WasmIonExitRegD1 = a1;
static constexpr Register WasmIonExitRegD2 = t0;

// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
static constexpr Register RegExpMatcherRegExpReg = CallTempReg0;
static constexpr Register RegExpMatcherStringReg = CallTempReg1;
static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2;

// Registerd used in RegExpTester instruction (do not use ReturnReg).
static constexpr Register RegExpTesterRegExpReg = CallTempReg0;
static constexpr Register RegExpTesterStringReg = CallTempReg1;
static constexpr Register RegExpTesterLastIndexReg = CallTempReg2;

static constexpr uint32_t CodeAlignment = 4;

// This boolean indicates whether we support SIMD instructions flavoured for
// this architecture or not. Rather than a method in the LIRGenerator, it is
// here such that it is accessible from the entire codebase. Once full support
// for SIMD is reached on all tier-1 platforms, this constant can be deleted.
static constexpr bool SupportsSimd = false;

// MIPS instruction types
//                +---------------------------------------------------------------+
//                |    6      |    5    |    5    |    5    |    5    |    6      |
//                +---------------------------------------------------------------+
// Register type  |  Opcode   |    Rs   |    Rt   |    Rd   |    Sa   | Function  |
//                +---------------------------------------------------------------+
//                |    6      |    5    |    5    |               16              |
//                +---------------------------------------------------------------+
// Immediate type |  Opcode   |    Rs   |    Rt   |    2's complement constant    |
//                +---------------------------------------------------------------+
//                |    6      |                        26                         |
//                +---------------------------------------------------------------+
// Jump type      |  Opcode   |                    jump_target                    |
//                +---------------------------------------------------------------+
//                31 bit                                                      bit 0

// MIPS instruction encoding constants.
static const uint32_t OpcodeShift = 26;
static const uint32_t OpcodeBits = 6;
static const uint32_t RSShift = 21;
static const uint32_t RSBits = 5;
static const uint32_t RTShift = 16;
static const uint32_t RTBits = 5;
static const uint32_t RDShift = 11;
static const uint32_t RDBits = 5;
static const uint32_t RZShift = 0;
static const uint32_t RZBits = 5;
static const uint32_t SAShift = 6;
static const uint32_t SABits = 5;
static const uint32_t FunctionShift = 0;
static const uint32_t FunctionBits = 6;
static const uint32_t Imm16Shift = 0;
static const uint32_t Imm16Bits = 16;
static const uint32_t Imm26Shift = 0;
static const uint32_t Imm26Bits = 26;
static const uint32_t Imm28Shift = 0;
static const uint32_t Imm28Bits = 28;
static const uint32_t ImmFieldShift = 2;
static const uint32_t FRBits = 5;
static const uint32_t FRShift = 21;
static const uint32_t FSShift = 11;
static const uint32_t FSBits = 5;
static const uint32_t FTShift = 16;
static const uint32_t FTBits = 5;
static const uint32_t FDShift = 6;
static const uint32_t FDBits = 5;
static const uint32_t FCccShift = 8;
static const uint32_t FCccBits = 3;
static const uint32_t FBccShift = 18;
static const uint32_t FBccBits = 3;
static const uint32_t FBtrueShift = 16;
static const uint32_t FBtrueBits = 1;
static const uint32_t FccMask = 0x7;
static const uint32_t FccShift = 2;


// MIPS instruction  field bit masks.
static const uint32_t OpcodeMask = ((1 << OpcodeBits) - 1) << OpcodeShift;
static const uint32_t Imm16Mask = ((1 << Imm16Bits) - 1) << Imm16Shift;
static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift;
static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift;
static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift;
static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift;
static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift;
static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift;
static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift;
static const uint32_t RegMask = Registers::Total - 1;

static const uint32_t BREAK_STACK_UNALIGNED = 1;
static const uint32_t MAX_BREAK_CODE = 1024 - 1;

class Instruction;
class InstReg;
class InstImm;
class InstJump;

uint32_t RS(Register r);
uint32_t RT(Register r);
uint32_t RT(uint32_t regCode);
uint32_t RT(FloatRegister r);
uint32_t RD(Register r);
uint32_t RD(FloatRegister r);
uint32_t RD(uint32_t regCode);
uint32_t RZ(Register r);
uint32_t RZ(FloatRegister r);
uint32_t SA(uint32_t value);
uint32_t SA(FloatRegister r);

Register toRS (Instruction& i);
Register toRT (Instruction& i);
Register toRD (Instruction& i);
Register toR (Instruction& i);

// MIPS enums for instruction fields
enum Opcode {
    op_special  = 0 << OpcodeShift,
    op_regimm   = 1 << OpcodeShift,

    op_j        = 2 << OpcodeShift,
    op_jal      = 3 << OpcodeShift,
    op_beq      = 4 << OpcodeShift,
    op_bne      = 5 << OpcodeShift,
    op_blez     = 6 << OpcodeShift,
    op_bgtz     = 7 << OpcodeShift,

    op_addi     = 8 << OpcodeShift,
    op_addiu    = 9 << OpcodeShift,
    op_slti     = 10 << OpcodeShift,
    op_sltiu    = 11 << OpcodeShift,
    op_andi     = 12 << OpcodeShift,
    op_ori      = 13 << OpcodeShift,
    op_xori     = 14 << OpcodeShift,
    op_lui      = 15 << OpcodeShift,

    op_cop1     = 17 << OpcodeShift,
    op_cop1x    = 19 << OpcodeShift,

    op_beql     = 20 << OpcodeShift,
    op_bnel     = 21 << OpcodeShift,
    op_blezl    = 22 << OpcodeShift,
    op_bgtzl    = 23 << OpcodeShift,

    op_daddi    = 24 << OpcodeShift,
    op_daddiu   = 25 << OpcodeShift,

    op_ldl      = 26 << OpcodeShift,
    op_ldr      = 27 << OpcodeShift,

    op_special2 = 28 << OpcodeShift,
    op_special3 = 31 << OpcodeShift,

    op_lb       = 32 << OpcodeShift,
    op_lh       = 33 << OpcodeShift,
    op_lwl      = 34 << OpcodeShift,
    op_lw       = 35 << OpcodeShift,
    op_lbu      = 36 << OpcodeShift,
    op_lhu      = 37 << OpcodeShift,
    op_lwr      = 38 << OpcodeShift,
    op_lwu      = 39 << OpcodeShift,
    op_sb       = 40 << OpcodeShift,
    op_sh       = 41 << OpcodeShift,
    op_swl      = 42 << OpcodeShift,
    op_sw       = 43 << OpcodeShift,
    op_sdl      = 44 << OpcodeShift,
    op_sdr      = 45 << OpcodeShift,
    op_swr      = 46 << OpcodeShift,

    op_ll       = 48 << OpcodeShift,
    op_lwc1     = 49 << OpcodeShift,
    op_lwc2     = 50 << OpcodeShift,
    op_ldc1     = 53 << OpcodeShift,
    op_ldc2     = 54 << OpcodeShift,
    op_ld       = 55 << OpcodeShift,

    op_sc       = 56 << OpcodeShift,
    op_swc1     = 57 << OpcodeShift,
    op_swc2     = 58 << OpcodeShift,
    op_sdc1     = 61 << OpcodeShift,
    op_sdc2     = 62 << OpcodeShift,
    op_sd       = 63 << OpcodeShift,
};

enum RSField {
    rs_zero  = 0 << RSShift,
    // cop1 encoding of RS field.
    rs_mfc1  = 0 << RSShift,
    rs_one   = 1 << RSShift,
    rs_dmfc1 = 1 << RSShift,
    rs_cfc1  = 2 << RSShift,
    rs_mfhc1 = 3 << RSShift,
    rs_mtc1  = 4 << RSShift,
    rs_dmtc1 = 5 << RSShift,
    rs_ctc1  = 6 << RSShift,
    rs_mthc1 = 7 << RSShift,
    rs_bc1   = 8 << RSShift,
    rs_s     = 16 << RSShift,
    rs_d     = 17 << RSShift,
    rs_w     = 20 << RSShift,
    rs_l     = 21 << RSShift,
    rs_ps    = 22 << RSShift
};

enum RTField {
    rt_zero   = 0 << RTShift,
    // regimm  encoding of RT field.
    rt_bltz   = 0 << RTShift,
    rt_bgez   = 1 << RTShift,
    rt_bltzal = 16 << RTShift,
    rt_bgezal = 17 << RTShift
};

enum FunctionField {
    // special encoding of function field.
    ff_sll         = 0,
    ff_movci       = 1,
    ff_srl         = 2,
    ff_sra         = 3,
    ff_sllv        = 4,
    ff_srlv        = 6,
    ff_srav        = 7,

    ff_jr          = 8,
    ff_jalr        = 9,
    ff_movz        = 10,
    ff_movn        = 11,
    ff_break       = 13,
    ff_sync        = 15,

    ff_mfhi        = 16,
    ff_mflo        = 18,

    ff_dsllv       = 20,
    ff_dsrlv       = 22,
    ff_dsrav       = 23,

    ff_mult        = 24,
    ff_multu       = 25,
    ff_div         = 26,
    ff_divu        = 27,
    ff_dmult       = 28,
    ff_dmultu      = 29,
    ff_ddiv        = 30,
    ff_ddivu       = 31,

    ff_add         = 32,
    ff_addu        = 33,
    ff_sub         = 34,
    ff_subu        = 35,
    ff_and         = 36,
    ff_or          = 37,
    ff_xor         = 38,
    ff_nor         = 39,

    ff_slt         = 42,
    ff_sltu        = 43,
    ff_dadd        = 44,
    ff_daddu       = 45,
    ff_dsub        = 46,
    ff_dsubu       = 47,

    ff_tge         = 48,
    ff_tgeu        = 49,
    ff_tlt         = 50,
    ff_tltu        = 51,
    ff_teq         = 52,
    ff_tne         = 54,
    ff_dsll        = 56,
    ff_dsrl        = 58,
    ff_dsra        = 59,
    ff_dsll32      = 60,
    ff_dsrl32      = 62,
    ff_dsra32      = 63,

    // special2 encoding of function field.
    ff_mul         = 2,
    ff_clz         = 32,
    ff_clo         = 33,
    ff_dclz        = 36,

    // special3 encoding of function field.
    ff_ext         = 0,
    ff_dextm       = 1,
    ff_dextu       = 2,
    ff_dext        = 3,
    ff_ins         = 4,
    ff_dinsm       = 5,
    ff_dinsu       = 6,
    ff_dins        = 7,
    ff_bshfl       = 32,

    // cop1 encoding of function field.
    ff_add_fmt     = 0,
    ff_sub_fmt     = 1,
    ff_mul_fmt     = 2,
    ff_div_fmt     = 3,
    ff_sqrt_fmt    = 4,
    ff_abs_fmt     = 5,
    ff_mov_fmt     = 6,
    ff_neg_fmt     = 7,

    ff_round_l_fmt = 8,
    ff_trunc_l_fmt = 9,
    ff_ceil_l_fmt  = 10,
    ff_floor_l_fmt = 11,

    ff_round_w_fmt = 12,
    ff_trunc_w_fmt = 13,
    ff_ceil_w_fmt  = 14,
    ff_floor_w_fmt = 15,

    ff_movf_fmt    = 17,
    ff_movz_fmt    = 18,
    ff_movn_fmt    = 19,

    ff_cvt_s_fmt   = 32,
    ff_cvt_d_fmt   = 33,
    ff_cvt_w_fmt   = 36,
    ff_cvt_l_fmt   = 37,
    ff_cvt_ps_s    = 38,

    ff_c_f_fmt     = 48,
    ff_c_un_fmt    = 49,
    ff_c_eq_fmt    = 50,
    ff_c_ueq_fmt   = 51,
    ff_c_olt_fmt   = 52,
    ff_c_ult_fmt   = 53,
    ff_c_ole_fmt   = 54,
    ff_c_ule_fmt   = 55,

    ff_madd_s      = 32,
    ff_madd_d      = 33,

    // Loongson encoding of function field.
    ff_gsxbx       = 0,
    ff_gsxhx       = 1,
    ff_gsxwx       = 2,
    ff_gsxdx       = 3,
    ff_gsxwlc1     = 4,
    ff_gsxwrc1     = 5,
    ff_gsxdlc1     = 6,
    ff_gsxdrc1     = 7,
    ff_gsxwxc1     = 6,
    ff_gsxdxc1     = 7,
    ff_gsxq        = 0x20,
    ff_gsxqc1      = 0x8020,

    ff_null        = 0
};

class Operand;

// A BOffImm16 is a 16 bit immediate that is used for branches.
class BOffImm16
{
    uint32_t data;

  public:
    uint32_t encode() {
        MOZ_ASSERT(!isInvalid());
        return data;
    }
    int32_t decode() {
        MOZ_ASSERT(!isInvalid());
        return (int32_t(data << 18) >> 16) + 4;
    }

    explicit BOffImm16(int offset)
      : data ((offset - 4) >> 2 & Imm16Mask)
    {
        MOZ_ASSERT((offset & 0x3) == 0);
        MOZ_ASSERT(IsInRange(offset));
    }
    static bool IsInRange(int offset) {
        if ((offset - 4) < int(unsigned(INT16_MIN) << 2))
            return false;
        if ((offset - 4) > (INT16_MAX << 2))
            return false;
        return true;
    }
    static const uint32_t INVALID = 0x00020000;
    BOffImm16()
      : data(INVALID)
    { }

    bool isInvalid() {
        return data == INVALID;
    }
    Instruction* getDest(Instruction* src) const;

    BOffImm16(InstImm inst);
};

// A JOffImm26 is a 26 bit immediate that is used for unconditional jumps.
class JOffImm26
{
    uint32_t data;

  public:
    uint32_t encode() {
        MOZ_ASSERT(!isInvalid());
        return data;
    }
    int32_t decode() {
        MOZ_ASSERT(!isInvalid());
        return int32_t(data << 8) >> 6;
    }

    explicit JOffImm26(int offset)
      : data (offset >> 2 & Imm26Mask)
    {
        MOZ_ASSERT((offset & 0x3) == 0);
    }
    static const uint32_t INVALID = 0x20000000;
    JOffImm26()
      : data(INVALID)
    { }

    bool isInvalid() {
        return data == INVALID;
    }
    Instruction* getDest(Instruction* src);

};

class Imm16
{
    uint16_t value;

  public:
    Imm16();
    Imm16(uint32_t imm)
      : value(imm)
    { }
    uint32_t encode() {
        return value;
    }
    int32_t decodeSigned() {
        return value;
    }
    uint32_t decodeUnsigned() {
        return value;
    }
    static bool IsInSignedRange(int32_t imm) {
        return imm >= INT16_MIN  && imm <= INT16_MAX;
    }
    static bool IsInUnsignedRange(uint32_t imm) {
        return imm <= UINT16_MAX ;
    }
    static Imm16 Lower (Imm32 imm) {
        return Imm16(imm.value & 0xffff);
    }
    static Imm16 Upper (Imm32 imm) {
        return Imm16((imm.value >> 16) & 0xffff);
    }
};

class Imm8
{
    uint8_t value;

  public:
    Imm8();
    Imm8(uint32_t imm)
      : value(imm)
    { }
    uint32_t encode(uint32_t shift) {
        return value << shift;
    }
    int32_t decodeSigned() {
        return value;
    }
    uint32_t decodeUnsigned() {
        return value;
    }
    static bool IsInSignedRange(int32_t imm) {
        return imm >= INT8_MIN  && imm <= INT8_MAX;
    }
    static bool IsInUnsignedRange(uint32_t imm) {
        return imm <= UINT8_MAX ;
    }
    static Imm8 Lower (Imm16 imm) {
        return Imm8(imm.decodeSigned() & 0xff);
    }
    static Imm8 Upper (Imm16 imm) {
        return Imm8((imm.decodeSigned() >> 8) & 0xff);
    }
};

class GSImm13
{
    uint16_t value;

  public:
    GSImm13();
    GSImm13(uint32_t imm)
      : value(imm & ~0xf)
    { }
    uint32_t encode(uint32_t shift) {
        return ((value >> 4) & 0x1f) << shift;
    }
    int32_t decodeSigned() {
        return value;
    }
    uint32_t decodeUnsigned() {
        return value;
    }
    static bool IsInRange(int32_t imm) {
        return imm >= int32_t(uint32_t(-256) << 4) && imm <= (255 << 4);
    }
};

class Operand
{
  public:
    enum Tag {
        REG,
        FREG,
        MEM
    };

  private:
    Tag tag : 3;
    uint32_t reg : 5;
    int32_t offset;

  public:
    Operand (Register reg_)
      : tag(REG), reg(reg_.code())
    { }

    Operand (FloatRegister freg)
      : tag(FREG), reg(freg.code())
    { }

    Operand (Register base, Imm32 off)
      : tag(MEM), reg(base.code()), offset(off.value)
    { }

    Operand (Register base, int32_t off)
      : tag(MEM), reg(base.code()), offset(off)
    { }

    Operand (const Address& addr)
      : tag(MEM), reg(addr.base.code()), offset(addr.offset)
    { }

    Tag getTag() const {
        return tag;
    }

    Register toReg() const {
        MOZ_ASSERT(tag == REG);
        return Register::FromCode(reg);
    }

    FloatRegister toFReg() const {
        MOZ_ASSERT(tag == FREG);
        return FloatRegister::FromCode(reg);
    }

    void toAddr(Register* r, Imm32* dest) const {
        MOZ_ASSERT(tag == MEM);
        *r = Register::FromCode(reg);
        *dest = Imm32(offset);
    }
    Address toAddress() const {
        MOZ_ASSERT(tag == MEM);
        return Address(Register::FromCode(reg), offset);
    }
    int32_t disp() const {
        MOZ_ASSERT(tag == MEM);
        return offset;
    }

    int32_t base() const {
        MOZ_ASSERT(tag == MEM);
        return reg;
    }
    Register baseReg() const {
        MOZ_ASSERT(tag == MEM);
        return Register::FromCode(reg);
    }
};

inline Imm32
Imm64::firstHalf() const
{
    return low();
}

inline Imm32
Imm64::secondHalf() const
{
    return hi();
}

void
PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
          ReprotectCode reprotect = DontReprotect);

void
PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target);

typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer;

class MIPSBufferWithExecutableCopy : public MIPSBuffer
{
  public:
    void executableCopy(uint8_t* buffer) {
        if (this->oom())
            return;

        for (Slice* cur = head; cur != nullptr; cur = cur->getNext()) {
            memcpy(buffer, &cur->instructions, cur->length());
            buffer += cur->length();
        }
    }

    bool appendBuffer(const MIPSBufferWithExecutableCopy& other) {
        if (this->oom())
            return false;

        for (Slice* cur = other.head; cur != nullptr; cur = cur->getNext()) {
            this->putBytes(cur->length(), &cur->instructions);
            if (this->oom())
                return false;
        }
        return true;
    }
};

class AssemblerMIPSShared : public AssemblerShared
{
  public:

    enum Condition {
        Equal,
        NotEqual,
        Above,
        AboveOrEqual,
        Below,
        BelowOrEqual,
        GreaterThan,
        GreaterThanOrEqual,
        LessThan,
        LessThanOrEqual,
        Overflow,
        CarrySet,
        CarryClear,
        Signed,
        NotSigned,
        Zero,
        NonZero,
        Always,
    };

    enum DoubleCondition {
        // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
        DoubleOrdered,
        DoubleEqual,
        DoubleNotEqual,
        DoubleGreaterThan,
        DoubleGreaterThanOrEqual,
        DoubleLessThan,
        DoubleLessThanOrEqual,
        // If either operand is NaN, these conditions always evaluate to true.
        DoubleUnordered,
        DoubleEqualOrUnordered,
        DoubleNotEqualOrUnordered,
        DoubleGreaterThanOrUnordered,
        DoubleGreaterThanOrEqualOrUnordered,
        DoubleLessThanOrUnordered,
        DoubleLessThanOrEqualOrUnordered
    };

    enum FPConditionBit {
        FCC0 = 0,
        FCC1,
        FCC2,
        FCC3,
        FCC4,
        FCC5,
        FCC6,
        FCC7
    };

    enum FPControl {
        FIR  = 0,
        UFR,
        UNFR = 4,
        FCCR = 25,
        FEXR,
        FENR = 28,
        FCSR = 31
    };

    enum FloatFormat {
        SingleFloat,
        DoubleFloat
    };

    enum JumpOrCall {
        BranchIsJump,
        BranchIsCall
    };

    enum FloatTestKind {
        TestForTrue,
        TestForFalse
    };

    struct MixedJumpPatch
    {
        enum Kind {
            NONE,
            PATCHABLE,
            CONDITIONAL,
        };

        BufferOffset src;
        BufferOffset mid;
        uintptr_t target;
        Kind kind;

        MixedJumpPatch(BufferOffset src, uintptr_t target, Kind kind)
          : src(src),
            mid(BufferOffset()),
            target(target),
            kind(kind)
        { }
    };

    // :( this should be protected, but since CodeGenerator
    // wants to use it, It needs to go out here :(

    BufferOffset nextOffset() {
        return m_buffer.nextOffset();
    }

  protected:
    Instruction * editSrc (BufferOffset bo) {
        return m_buffer.getInst(bo);
    }
  public:
    uint32_t actualIndex(uint32_t) const;
    static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index);
  protected:
    // structure for fixing up pc-relative loads/jumps when a the machine code
    // gets moved (executable copy, gc, etc.)
    struct RelativePatch
    {
        // the offset within the code buffer where the value is loaded that
        // we want to fix-up
        BufferOffset offset;
        void* target;
        Relocation::Kind kind;

        RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind)
          : offset(offset),
            target(target),
            kind(kind)
        { }
    };

    js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
    js::Vector<MixedJumpPatch, 8, SystemAllocPolicy> mixedJumps_;

    CompactBufferWriter jumpRelocations_;
    CompactBufferWriter dataRelocations_;
    CompactBufferWriter preBarriers_;

    MIPSBufferWithExecutableCopy m_buffer;

  public:
    AssemblerMIPSShared()
      : m_buffer(),
        isFinished(false)
    { }

    static Condition InvertCondition(Condition cond);
    static DoubleCondition InvertCondition(DoubleCondition cond);

    void writeRelocation(BufferOffset src) {
        jumpRelocations_.writeUnsigned(src.getOffset());
    }

    // As opposed to x86/x64 version, the data relocation has to be executed
    // before to recover the pointer, and not after.
    void writeDataRelocation(ImmGCPtr ptr) {
        if (ptr.value) {
            if (gc::IsInsideNursery(ptr.value))
                embedsNurseryPointers_ = true;
            dataRelocations_.writeUnsigned(nextOffset().getOffset());
        }
    }
    void writePrebarrierOffset(CodeOffset label) {
        preBarriers_.writeUnsigned(label.offset());
    }

  public:
    bool oom() const;

    void setPrinter(Sprinter* sp) {
    }

    static const Register getStackPointer() {
        return StackPointer;
    }

  protected:
    bool isFinished;
  public:
    void finish();
    bool asmMergeWith(const AssemblerMIPSShared& other);
    // Copy the assembly code to the given buffer, and perform any pending
    // relocations relying on the target address.
    void executableCopy(uint8_t* buffer);
    void copyJumpRelocationTable(uint8_t* dest);
    void copyDataRelocationTable(uint8_t* dest);
    void copyPreBarrierTable(uint8_t* dest);

    // Size of the instruction stream, in bytes.
    size_t size() const;
    // Size of the jump relocation table, in bytes.
    size_t jumpRelocationTableBytes() const;
    size_t dataRelocationTableBytes() const;
    size_t preBarrierTableBytes() const;

    // Size of the data table, in bytes.
    size_t bytesNeeded() const;

    // Write a blob of binary into the instruction stream *OR*
    // into a destination address. If dest is nullptr (the default), then the
    // instruction gets written into the instruction stream. If dest is not null
    // it is interpreted as a pointer to the location that we want the
    // instruction to be written.
    BufferOffset writeInst(uint32_t x, uint32_t* dest = nullptr);
    // A static variant for the cases where we don't want to have an assembler
    // object at all. Normally, you would use the dummy (nullptr) object.
    static void WriteInstStatic(uint32_t x, uint32_t* dest);

  public:
    BufferOffset haltingAlign(int alignment);
    BufferOffset nopAlign(int alignment);
    BufferOffset as_nop();

    // Branch and jump instructions
    BufferOffset as_bal(BOffImm16 off);
    BufferOffset as_b(BOffImm16 off);

    InstImm getBranchCode(JumpOrCall jumpOrCall);
    InstImm getBranchCode(Register s, Register t, Condition c);
    InstImm getBranchCode(Register s, Condition c);
    InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc);

    BufferOffset as_j(JOffImm26 off);
    BufferOffset as_jal(JOffImm26 off);

    BufferOffset as_jr(Register rs);
    BufferOffset as_jalr(Register rs);

    // Arithmetic instructions
    BufferOffset as_addu(Register rd, Register rs, Register rt);
    BufferOffset as_addiu(Register rd, Register rs, int32_t j);
    BufferOffset as_daddu(Register rd, Register rs, Register rt);
    BufferOffset as_daddiu(Register rd, Register rs, int32_t j);
    BufferOffset as_subu(Register rd, Register rs, Register rt);
    BufferOffset as_dsubu(Register rd, Register rs, Register rt);
    BufferOffset as_mult(Register rs, Register rt);
    BufferOffset as_multu(Register rs, Register rt);
    BufferOffset as_dmult(Register rs, Register rt);
    BufferOffset as_dmultu(Register rs, Register rt);
    BufferOffset as_div(Register rs, Register rt);
    BufferOffset as_divu(Register rs, Register rt);
    BufferOffset as_mul(Register rd, Register rs, Register rt);
    BufferOffset as_ddiv(Register rs, Register rt);
    BufferOffset as_ddivu(Register rs, Register rt);

    // Logical instructions
    BufferOffset as_and(Register rd, Register rs, Register rt);
    BufferOffset as_or(Register rd, Register rs, Register rt);
    BufferOffset as_xor(Register rd, Register rs, Register rt);
    BufferOffset as_nor(Register rd, Register rs, Register rt);

    BufferOffset as_andi(Register rd, Register rs, int32_t j);
    BufferOffset as_ori(Register rd, Register rs, int32_t j);
    BufferOffset as_xori(Register rd, Register rs, int32_t j);
    BufferOffset as_lui(Register rd, int32_t j);

    // Shift instructions
    // as_sll(zero, zero, x) instructions are reserved as nop
    BufferOffset as_sll(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsll(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsll32(Register rd, Register rt, uint16_t sa);
    BufferOffset as_sllv(Register rd, Register rt, Register rs);
    BufferOffset as_dsllv(Register rd, Register rt, Register rs);
    BufferOffset as_srl(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsrl(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsrl32(Register rd, Register rt, uint16_t sa);
    BufferOffset as_srlv(Register rd, Register rt, Register rs);
    BufferOffset as_dsrlv(Register rd, Register rt, Register rs);
    BufferOffset as_sra(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsra(Register rd, Register rt, uint16_t sa);
    BufferOffset as_dsra32(Register rd, Register rt, uint16_t sa);
    BufferOffset as_srav(Register rd, Register rt, Register rs);
    BufferOffset as_rotr(Register rd, Register rt, uint16_t sa);
    BufferOffset as_rotrv(Register rd, Register rt, Register rs);
    BufferOffset as_dsrav(Register rd, Register rt, Register rs);
    BufferOffset as_drotr(Register rd, Register rt, uint16_t sa);
    BufferOffset as_drotr32(Register rd, Register rt, uint16_t sa);
    BufferOffset as_drotrv(Register rd, Register rt, Register rs);

    // Load and store instructions
    BufferOffset as_lb(Register rd, Register rs, int16_t off);
    BufferOffset as_lbu(Register rd, Register rs, int16_t off);
    BufferOffset as_lh(Register rd, Register rs, int16_t off);
    BufferOffset as_lhu(Register rd, Register rs, int16_t off);
    BufferOffset as_lw(Register rd, Register rs, int16_t off);
    BufferOffset as_lwu(Register rd, Register rs, int16_t off);
    BufferOffset as_lwl(Register rd, Register rs, int16_t off);
    BufferOffset as_lwr(Register rd, Register rs, int16_t off);
    BufferOffset as_ll(Register rd, Register rs, int16_t off);
    BufferOffset as_ld(Register rd, Register rs, int16_t off);
    BufferOffset as_ldl(Register rd, Register rs, int16_t off);
    BufferOffset as_ldr(Register rd, Register rs, int16_t off);
    BufferOffset as_sb(Register rd, Register rs, int16_t off);
    BufferOffset as_sh(Register rd, Register rs, int16_t off);
    BufferOffset as_sw(Register rd, Register rs, int16_t off);
    BufferOffset as_swl(Register rd, Register rs, int16_t off);
    BufferOffset as_swr(Register rd, Register rs, int16_t off);
    BufferOffset as_sc(Register rd, Register rs, int16_t off);
    BufferOffset as_sd(Register rd, Register rs, int16_t off);
    BufferOffset as_sdl(Register rd, Register rs, int16_t off);
    BufferOffset as_sdr(Register rd, Register rs, int16_t off);

    // Loongson-specific load and store instructions
    BufferOffset as_gslbx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gssbx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gslhx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gsshx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gslwx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gsswx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gsldx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gssdx(Register rd, Register rs, Register ri, int16_t off);
    BufferOffset as_gslq(Register rh, Register rl, Register rs, int16_t off);
    BufferOffset as_gssq(Register rh, Register rl, Register rs, int16_t off);

    // Move from HI/LO register.
    BufferOffset as_mfhi(Register rd);
    BufferOffset as_mflo(Register rd);

    // Set on less than.
    BufferOffset as_slt(Register rd, Register rs, Register rt);
    BufferOffset as_sltu(Register rd, Register rs, Register rt);
    BufferOffset as_slti(Register rd, Register rs, int32_t j);
    BufferOffset as_sltiu(Register rd, Register rs, uint32_t j);

    // Conditional move.
    BufferOffset as_movz(Register rd, Register rs, Register rt);
    BufferOffset as_movn(Register rd, Register rs, Register rt);
    BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0);
    BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0);

    // Bit twiddling.
    BufferOffset as_clz(Register rd, Register rs);
    BufferOffset as_dclz(Register rd, Register rs);
    BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dins(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dinsm(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dinsu(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dext(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dextm(Register rt, Register rs, uint16_t pos, uint16_t size);
    BufferOffset as_dextu(Register rt, Register rs, uint16_t pos, uint16_t size);

    // Sign extend
    BufferOffset as_seb(Register rd, Register rt);
    BufferOffset as_seh(Register rd, Register rt);

    // FP instructions

    // Use these two functions only when you are sure address is aligned.
    // Otherwise, use ma_ld and ma_sd.
    BufferOffset as_ld(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_sd(FloatRegister fd, Register base, int32_t off);

    BufferOffset as_ls(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_ss(FloatRegister fd, Register base, int32_t off);

    // Loongson-specific FP load and store instructions
    BufferOffset as_gsldl(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gsldr(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gssdl(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gssdr(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gslsl(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gslsr(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gsssl(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gsssr(FloatRegister fd, Register base, int32_t off);
    BufferOffset as_gslsx(FloatRegister fd, Register rs, Register ri, int16_t off);
    BufferOffset as_gsssx(FloatRegister fd, Register rs, Register ri, int16_t off);
    BufferOffset as_gsldx(FloatRegister fd, Register rs, Register ri, int16_t off);
    BufferOffset as_gssdx(FloatRegister fd, Register rs, Register ri, int16_t off);
    BufferOffset as_gslq(FloatRegister rh, FloatRegister rl, Register rs, int16_t off);
    BufferOffset as_gssq(FloatRegister rh, FloatRegister rl, Register rs, int16_t off);

    BufferOffset as_movs(FloatRegister fd, FloatRegister fs);
    BufferOffset as_movd(FloatRegister fd, FloatRegister fs);

    BufferOffset as_ctc1(Register rt, FPControl fc);
    BufferOffset as_cfc1(Register rt, FPControl fc);

    BufferOffset as_mtc1(Register rt, FloatRegister fs);
    BufferOffset as_mfc1(Register rt, FloatRegister fs);

    BufferOffset as_mthc1(Register rt, FloatRegister fs);
    BufferOffset as_mfhc1(Register rt, FloatRegister fs);
    BufferOffset as_dmtc1(Register rt, FloatRegister fs);
    BufferOffset as_dmfc1(Register rt, FloatRegister fs);

  public:
    // FP convert instructions
    BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs);
    BufferOffset as_floorws(FloatRegister fd, FloatRegister fs);
    BufferOffset as_roundws(FloatRegister fd, FloatRegister fs);
    BufferOffset as_truncws(FloatRegister fd, FloatRegister fs);
    BufferOffset as_truncls(FloatRegister fd, FloatRegister fs);

    BufferOffset as_ceilwd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_floorwd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_roundwd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_truncwd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_truncld(FloatRegister fd, FloatRegister fs);

    BufferOffset as_cvtdl(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtds(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtdw(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtld(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtls(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtsd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtsl(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtsw(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtwd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_cvtws(FloatRegister fd, FloatRegister fs);

    // FP arithmetic instructions
    BufferOffset as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft);

    BufferOffset as_abss(FloatRegister fd, FloatRegister fs);
    BufferOffset as_absd(FloatRegister fd, FloatRegister fs);
    BufferOffset as_negs(FloatRegister fd, FloatRegister fs);
    BufferOffset as_negd(FloatRegister fd, FloatRegister fs);

    BufferOffset as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft);
    BufferOffset as_sqrts(FloatRegister fd, FloatRegister fs);
    BufferOffset as_sqrtd(FloatRegister fd, FloatRegister fs);

    // FP compare instructions
    BufferOffset as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                       FPConditionBit fcc = FCC0);
    BufferOffset as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                        FPConditionBit fcc = FCC0);
    BufferOffset as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                        FPConditionBit fcc = FCC0);
    BufferOffset as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                         FPConditionBit fcc = FCC0);

    // FP conditional move.
    BufferOffset as_movt(FloatFormat fmt, FloatRegister fd, FloatRegister fs,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_movf(FloatFormat fmt, FloatRegister fd, FloatRegister fs,
                         FPConditionBit fcc = FCC0);
    BufferOffset as_movz(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt);
    BufferOffset as_movn(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt);

    // label operations
    void bind(Label* label, BufferOffset boff = BufferOffset());
    void bindLater(Label* label, wasm::TrapDesc target);
    void bind(InstImm* inst, uintptr_t branch, uintptr_t target);
    virtual void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) = 0;
    void bind(RepatchLabel* label);
    void bind(CodeOffset* label) {
        label->bind(currentOffset());
    }
    uint32_t currentOffset() {
        return nextOffset().getOffset();
    }
    void retarget(Label* label, Label* target);

    // See Bind
    size_t labelToPatchOffset(CodeOffset label) { return label.offset(); }

    void call(Label* label);
    void call(void* target);

    void as_break(uint32_t code);
    void as_sync(uint32_t stype = 0);

  public:
    static bool SupportsFloatingPoint() {
#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || \
    defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
        return true;
#else
        return false;
#endif
    }
    static bool SupportsUnalignedAccesses() {
        return true;
    }
    static bool SupportsSimd() {
        return js::jit::SupportsSimd;
    }

  protected:
    InstImm invertBranch(InstImm branch, BOffImm16 skipOffset);
    void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
        enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind));
        if (kind == Relocation::JITCODE)
            writeRelocation(src);
    }

    void addMixedJump(BufferOffset src, uintptr_t target,
                      MixedJumpPatch::Kind kind = MixedJumpPatch::NONE)
    {
        enoughMemory_ &= mixedJumps_.append(MixedJumpPatch(src, target, kind));
    }

    virtual void GenerateMixedJumps() = 0;
    void PatchMixedJumps(uint8_t* buffer);

  public:
    size_t numMixedJumps() const {
        return mixedJumps_.length();
    }
    MixedJumpPatch& mixedJump(size_t i) {
        return mixedJumps_[i];
    }

    void flushBuffer() {
    }

    void comment(const char* msg) {
        // This is not implemented because setPrinter() is not implemented.
        // TODO spew("; %s", msg);
    }

    static uint32_t NopSize() { return 4; }
    static uint32_t PatchWrite_NearCallSize();

    static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
    static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);

    static uint32_t AlignDoubleArg(uint32_t offset) {
        return (offset + 1U) &~ 1U;
    }

    static uint8_t* NextInstruction(uint8_t* instruction, uint32_t* count = nullptr);
    static Instruction* GetInstructionImmediateFromJump(Instruction* jump);
    static void PatchMixedJump(uint8_t* src, uint8_t* mid, uint8_t* target);

    static void ToggleToJmp(CodeLocationLabel inst_);
    static void ToggleToCmp(CodeLocationLabel inst_);

    static void UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value);

    void processCodeLabels(uint8_t* rawCode);

    bool bailed() {
        return m_buffer.bail();
    }

    void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
                                     const Disassembler::HeapAccess& heapAccess)
    {
        // Implement this if we implement a disassembler.
    }
}; // AssemblerMIPSShared

// sll zero, zero, 0
const uint32_t NopInst = 0x00000000;

// An Instruction is a structure for both encoding and decoding any and all
// MIPS instructions.
class Instruction
{
  protected:
    uint32_t data;

    // Standard constructor
    Instruction (uint32_t data_) : data(data_) { }

    // You should never create an instruction directly.  You should create a
    // more specific instruction which will eventually call one of these
    // constructors for you.
  public:
    uint32_t encode() const {
        return data;
    }

    void makeNop() {
        data = NopInst;
    }

    void setData(uint32_t data) {
        this->data = data;
    }

    const Instruction & operator=(const Instruction& src) {
        data = src.data;
        return *this;
    }

    // Extract the one particular bit.
    uint32_t extractBit(uint32_t bit) {
        return (encode() >> bit) & 1;
    }
    // Extract a bit field out of the instruction
    uint32_t extractBitField(uint32_t hi, uint32_t lo) {
        return (encode() >> lo) & ((2 << (hi - lo)) - 1);
    }
    // Since all MIPS instructions have opcode, the opcode
    // extractor resides in the base class.
    uint32_t extractOpcode() {
        return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift);
    }
    // Return the fields at their original place in the instruction encoding.
    Opcode OpcodeFieldRaw() const {
        return static_cast<Opcode>(encode() & OpcodeMask);
    }

    // Get the next instruction in the instruction stream.
    // This does neat things like ignoreconstant pools and their guards.
    Instruction* next();

    // Sometimes, an api wants a uint32_t (or a pointer to it) rather than
    // an instruction.  raw() just coerces this into a pointer to a uint32_t
    const uint32_t* raw() const { return &data; }
    uint32_t size() const { return 4; }
}; // Instruction

// make sure that it is the right size
static_assert(sizeof(Instruction) == 4, "Size of Instruction class has to be 4 bytes.");

class InstNOP : public Instruction
{
  public:
    InstNOP()
      : Instruction(NopInst)
    { }

};

// Class for register type instructions.
class InstReg : public Instruction
{
  public:
    InstReg(Opcode op, Register rd, FunctionField ff)
      : Instruction(op | RD(rd) | ff)
    { }
    InstReg(Opcode op, Register rs, Register rt, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | ff)
    { }
    InstReg(Opcode op, Register rs, Register rt, Register rd, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RD(rd) | ff)
    { }
    InstReg(Opcode op, Register rs, Register rt, Register rd, uint32_t sa, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RD(rd) | SA(sa) | ff)
    { }
    InstReg(Opcode op, RSField rs, Register rt, Register rd, uint32_t sa, FunctionField ff)
      : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff)
    { }
    InstReg(Opcode op, Register rs, RTField rt, Register rd, uint32_t sa, FunctionField ff)
      : Instruction(op | RS(rs) | rt | RD(rd) | SA(sa) | ff)
    { }
    InstReg(Opcode op, Register rs, uint32_t cc, Register rd, uint32_t sa, FunctionField ff)
      : Instruction(op | RS(rs) | cc | RD(rd) | SA(sa) | ff)
    { }
    InstReg(Opcode op, uint32_t code, FunctionField ff)
      : Instruction(op | code | ff)
    { }
    // for float point
    InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd)
      : Instruction(op | rs | RT(rt) | RD(rd))
    { }
    InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd, uint32_t sa, FunctionField ff)
      : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff)
    { }
    InstReg(Opcode op, RSField rs, Register rt, FloatRegister fs, FloatRegister fd, FunctionField ff)
      : Instruction(op | rs | RT(rt) | RD(fs) | SA(fd) | ff)
    { }
    InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fs, FloatRegister fd, FunctionField ff)
      : Instruction(op | rs | RT(ft) | RD(fs) | SA(fd) | ff)
    { }
    InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fd, uint32_t sa, FunctionField ff)
      : Instruction(op | rs | RT(ft) | RD(fd) | SA(sa) | ff)
    { }

    uint32_t extractRS () {
        return extractBitField(RSShift + RSBits - 1, RSShift);
    }
    uint32_t extractRT () {
        return extractBitField(RTShift + RTBits - 1, RTShift);
    }
    uint32_t extractRD () {
        return extractBitField(RDShift + RDBits - 1, RDShift);
    }
    uint32_t extractSA () {
        return extractBitField(SAShift + SABits - 1, SAShift);
    }
    uint32_t extractFunctionField () {
        return extractBitField(FunctionShift + FunctionBits - 1, FunctionShift);
    }
};

// Class for branch, load and store instructions with immediate offset.
class InstImm : public Instruction
{
  public:
    void extractImm16(BOffImm16* dest);

    InstImm(Opcode op, Register rs, Register rt, BOffImm16 off)
      : Instruction(op | RS(rs) | RT(rt) | off.encode())
    { }
    InstImm(Opcode op, Register rs, RTField rt, BOffImm16 off)
      : Instruction(op | RS(rs) | rt | off.encode())
    { }
    InstImm(Opcode op, RSField rs, uint32_t cc, BOffImm16 off)
      : Instruction(op | rs | cc | off.encode())
    { }
    InstImm(Opcode op, Register rs, Register rt, Imm16 off)
      : Instruction(op | RS(rs) | RT(rt) | off.encode())
    { }
    InstImm(uint32_t raw)
      : Instruction(raw)
    { }
    // For floating-point loads and stores.
    InstImm(Opcode op, Register rs, FloatRegister rt, Imm16 off)
      : Instruction(op | RS(rs) | RT(rt) | off.encode())
    { }

    uint32_t extractOpcode() {
        return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift);
    }
    void setOpcode(Opcode op) {
        data = (data & ~OpcodeMask) | op;
    }
    uint32_t extractRS() {
        return extractBitField(RSShift + RSBits - 1, RSShift);
    }
    uint32_t extractRT() {
        return extractBitField(RTShift + RTBits - 1, RTShift);
    }
    void setRT(RTField rt) {
        data = (data & ~RTMask) | rt;
    }
    uint32_t extractImm16Value() {
        return extractBitField(Imm16Shift + Imm16Bits - 1, Imm16Shift);
    }
    void setBOffImm16(BOffImm16 off) {
        // Reset immediate field and replace it
        data = (data & ~Imm16Mask) | off.encode();
    }
    void setImm16(Imm16 off) {
        // Reset immediate field and replace it
        data = (data & ~Imm16Mask) | off.encode();
    }
};

// Class for Jump type instructions.
class InstJump : public Instruction
{
  public:
    InstJump(Opcode op, JOffImm26 off)
      : Instruction(op | off.encode())
    { }

    uint32_t extractImm26Value() {
        return extractBitField(Imm26Shift + Imm26Bits - 1, Imm26Shift);
    }
    void setJOffImm26(JOffImm26 off) {
        // Reset immediate field and replace it
        data = (data & ~Imm26Mask) | off.encode();
    }
};

// Class for Loongson-specific instructions
class InstGS : public Instruction
{
  public:
    // For indexed loads and stores.
    InstGS(Opcode op, Register rs, Register rt, Register rd, Imm8 off, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RD(rd) | off.encode(3) | ff)
    { }
    InstGS(Opcode op, Register rs, FloatRegister rt, Register rd, Imm8 off, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RD(rd) | off.encode(3) | ff)
    { }
    // For quad-word loads and stores.
    InstGS(Opcode op, Register rs, Register rt, Register rz, GSImm13 off, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RZ(rz) | off.encode(6) | ff)
    { }
    InstGS(Opcode op, Register rs, FloatRegister rt, FloatRegister rz, GSImm13 off, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | RZ(rz) | off.encode(6) | ff)
    { }
    InstGS(uint32_t raw)
      : Instruction(raw)
    { }
    // For floating-point unaligned loads and stores.
    InstGS(Opcode op, Register rs, FloatRegister rt, Imm8 off, FunctionField ff)
      : Instruction(op | RS(rs) | RT(rt) | off.encode(6) | ff)
    { }
};

inline bool
IsUnaligned(const wasm::MemoryAccessDesc& access)
{
    if (!access.align())
        return false;

#ifdef JS_CODEGEN_MIPS32
    if (access.type() == Scalar::Int64 && access.align() >= 4)
        return false;
#endif

    return access.align() < access.byteSize();
}

} // namespace jit
} // namespace js

#endif /* jit_mips_shared_Assembler_mips_shared_h */
